<?php
class User {

    public function __construct() {
        $this->SQL = MySQL::getInstance();
        $this->diffMgr = new Difficulty();
        $this->msgMgr = new Msg();
    }

    /*
     * login - verify password and set session
     * @param username string username
     * @param password string password (not hashed)
     * @return bool TRUE/FALSE
     */
    public function login($username, $password) {
        $userInfo = $this->SQL->fetch_single('
            SELECT *
            FROM users
            WHERE username = "%1$s"
            AND SHA(CONCAT("%2$s", "%3$s")) = password
            LIMIT 1',
            $username,
            $password,
            PASSWORD_SALT
        );
        if($userInfo) {
            $_SESSION['user'] = array(
                'id'            => $userInfo['id'],
                'username'      => $userInfo['username'],
                'name'          => $userInfo['name'],
                'language_id'   => $userInfo['language_id'],
                'avatar'        => $this->loadGravatar($userInfo['username']),
            );
            return TRUE;
        } else {
            return FALSE;
        }
    }

    /*
     * logout - destroy session, logging out current user
     * @return bool TRUE/FALSE
     */
    public function logout() {
        unset($_SESSION['user']);
        session_destroy();
        return TRUE;
    }

    /*
     * register - create a new user (includes data validation)
     * @param data array register form data: name (checked for unique), password, email (email validity is checked)
     * @return mixed FALSE on success, string with error message on fail
     */
    public function register($data) {
        if(!isset($data['email']) || !isset($data['name']) || !isset($data['password']) || !$data['email'] || !$data['name'] || !$data['password']) {
            return $this->msgMgr->get('register-incomplete-error');
        }
        if(!preg_match('/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/', $data['email'])) {
            return $this->msgMgr->get('register-email-error');
        }
        if($this->exists($data['email'])) {
            return $this->msgMgr->get('register-exists-error');
        }

        $password_hash = sha1($data['password'] . PASSWORD_SALT);
        $user_id = $this->SQL->fquery('
            INSERT INTO users
            (username, password, name, language_id, registered)
            VALUES
            ("%s", "%s", "%s", %d, NOW())',
            addslashes($data['email']),
            $password_hash,
            addslashes($data['name']),
            $data['language_id']
        );
        if($user_id) {
            $this->msgMgr->sendRegistrationMail($data['email']);
            $this->msgMgr->sendAdminRegistrationMail($data);
            return FALSE;
        }
    }

    /*
     * exists - check if a user account exists with a given email address. used for registration and password resetting
     * @param email straing email address
     * @return bool TRUE/FALSE
     */
    public function exists($email) {
        $ret = $this->SQL->fetch_value('
            SELECT id
            FROM users
            WHERE username = "%s"
            AND password IS NOT NULL
            AND password != ""
            LIMIT 1',
            addslashes($email)
        );
        return $ret ? TRUE : FALSE;
    }

    /*
     * update - update userinfo on account (username, name, password)
     * @param id int current user id
     * @param data array form data: username, name, old password, new password (all must be set, only nonblank values are updated)
     * @return mixed FALSE on success, string with error message on fail
     */
    public function update($id, $data) {
        $ret = $this->SQL->fquery('
            UPDATE users
            SET username = "%s",
                name = "%s",
                language_id = %d
            WHERE id = %d
            LIMIT 1',
            addslashes($data['username']),
            addslashes($data['name']),
            $data['language_id'],
            $id
        );
        if(!$ret) {
            return $this->msgMgr->get('user-update-error');
        }

        if(isset($data['password_new']) && isset($data['password_old']) && $data['password_new'] && $data['password_old']) {
            $old_hash = $this->SQL->fetch_value('
                SELECT password
                FROM users
                WHERE id = %d
                LIMIT 1',
                $id
            );
            if($old_hash === sha1($data['password_old'] . PASSWORD_SALT)) {
                $ret = $this->SQL->fquery('
                    UPDATE users
                    SET password = "%s"
                    WHERE id = %d
                    LIMIT 1',
                    sha1($data['password_new'] . PASSWORD_SALT),
                    $id
                );
                if($ret) {
                    return FALSE;
                } else {
                    return $this->msgMgr->get('password-change-error');
                }
            } else {
                return $this->msgMgr->get('password-old-error');
            }
        }
        return FALSE;
    }

    /*
     * search - search for users with matching username/name
     * @param currentId int current user id
     * @param query string search query
     * @return array array of matching users, form array(id, name)
     */
    public function search($currentId, $query) {
        return $this->SQL->fquery('
            SELECT id, name
            FROM users
            WHERE password != ""
            AND id != %2$d
            AND (
                name LIKE "%%%1$s%%"
                OR username LIKE "%%%1$s%%"
            )',
            addslashes($query),
            $currentId
        );
    }

    /*
     * getFriends - get list of a user's friends (people who he follows)
     * @param id int user id
     * @return array users, array of form array(id, username, name)
     */
    public function getFriends($id) {
        return $this->SQL->fquery('
            SELECT u.id, u.username, u.name
            FROM friends f
            JOIN users u
                ON u.id = f.friend_id
            WHERE f.user_id = %d',
            $id
        );
    }

    /*
     * delete - deactivate a user: delete all his friends/followers and unset password so he can't login
     * @param id int user id
     * @return bool TRUE/FALSE
     */
    public function delete($id) {
        $this->SQL->fquery('
            DELETE FROM friends
            WHERE user_id = %1$d
            OR friend_id = %1$d',
            $id
        );
        return $this->SQL->fquery('
            UPDATE users
            SET password = ""
            WHERE id = %d
            LIMIT 1',
            $id
        );
    }

    /*
     * get - get basic userinfo for a user: id, username, name, registration date, friend count, active status, and if he can be friended by current user
     * @param id int user id
     * @param currentId int current user id
     * @return array userinfo, form array(id, username, name, friend_id, registered, friend_count, active)
     */
    public function get($id, $currentId = 0) {
        return $this->SQL->fetch_single('
            SELECT u.id, u.username, u.name, COALESCE(f.friend_id, 0) AS friend_id, u.registered, COUNT(f2.friend_id) AS friend_count,
                IF(u.password = "", 0, 1) AS active, IF(f3.user_id IS NOT NULL, 1, 0) AS is_follower
            FROM users u
            LEFT JOIN friends f
                ON f.user_id = %1$d
                AND f.friend_id = u.id
            LEFT JOIN friends f2
                ON f2.user_id = %2$d
            LEFT JOIN friends f3
                ON f3.user_id = u.id
                AND f3.friend_id = %1$d
            WHERE u.id = %2$d
            LIMIT 1',
            $currentId,
            $id
        );
    }

    /*
     * getUserFeed - get user feed
     * @param id int user id
     * @param days int max number of days to return items for
     * @param noFriends bool display checkins from friends or not
     * @return array nested array of checkins
     */
    public function getUserFeed($id, $days, $noFriends = FALSE) {
        if(!$noFriends) {
            $friends = '
                OR url.user_id IN (
                    SELECT friend_id
                    FROM friends
                    WHERE user_id = ' . (int)$id .
                ')';
        } else {
            $friends = FALSE;
        }
        $days = 'AND CAST(url.timestamp AS DATE) >= CURDATE() - INTERVAL ' . (int)$days . ' DAY';

        return $this->SQL->fquery('
            SELECT url.id, url.user_id, url.route_id, url.timestamp, url.completed, url.comment, u.name,
               r.is_short, l.id AS location_id, l.name AS location, c.name AS colour, c.language_key AS colour_lang, d.name AS difficulty, r.name AS route_name,
               rt.name AS type_name, rt.type AS route_type, rt.unicode
            FROM users_routes_link url
            JOIN users u
                ON u.id = url.user_id
            JOIN routes r
                ON r.id = url.route_id
            JOIN route_types rt
                ON rt.id = r.type_id
            JOIN locations l
                ON l.id = r.location_id
            JOIN colours c
                ON c.id = r.colour_id
            JOIN difficulties d
                ON d.id = r.difficulty_id

            WHERE (
                url.user_id = %1$d
                %2$s
            )
            %3$s
            ORDER BY DATE(url.timestamp) DESC, l.id ASC, url.user_id',
            $id,
            $friends,
            $days
        );
    }

    /*
     * getUserRouteStats - get a user's climbing stats: count, unique routes, amount completed
     * @param id int user id
     * @return array route info of form array(count, route_count, completed)
     */
    public function getUserRouteStats($id) {
        //amount of routes and unique routes that were scaled, with or withouth block or failed
        $ret = $this->SQL->fetch_single('
            SELECT COUNT(*) AS `count`, COUNT(DISTINCT(route_id)) AS route_count
            FROM users_routes_link
            WHERE user_id = %d
            LIMIT 1',
            $id
        );
        //amount of routes that were scaled, with or without block
        $ret['completed'] = $this->SQL->fetch_value('
            SELECT COUNT(*)
            FROM users_routes_link
            WHERE user_id = %d
            AND completed >= 0
            LIMIT 1',
            $id
        );
        //amount of routes that were scaled, with blocks
        $ret['blocked'] = $this->SQL->fetch_value('
            SELECT COUNT(*)
            FROM users_routes_link
            WHERE user_id = %d
            AND completed > 0
            LIMIT 1',
            $id
        );
        return $ret;
    }

    /*
     * getUserDifficultyRange - get difficulty range stats for a user
     * @param id int user id
     * @return array difficulty range of form array(value, name, count)
     */
    public function getUserDifficultyRange($id) {
        return $this->SQL->fquery('
            SELECT d.id, d.value, d.name, COUNT(*) AS `count`
            FROM users_routes_link url
            JOIN routes r
                ON r.id = url.route_id
            JOIN difficulties d
                ON d.id = r.difficulty_id
            WHERE url.user_id = %d
            AND url.completed >= 0
            GROUP BY d.value
            ORDER BY d.value',
            $id
        );
    }

    /*
     * getUserMaxDifficultyCompleted - get the highest difficulty route a user has successfully climbed
     * @param id int user id
     * @return int difficulty id (not name)
     */
    public function getUserMaxDifficultyCompleted($id) {
        $diff = $this->diffMgr->getAll();
        $difficulties = array();
        foreach($diff as $row) {
            $difficulties[$row['value']] = $row['name'];
        }

        $alltime = $this->SQL->fetch_value('
            SELECT MAX(d.value)
            FROM users_routes_link url
            JOIN routes r
                ON r.id = url.route_id
            JOIN difficulties d
                ON d.id = r.difficulty_id
            WHERE url.user_id = %d
            AND url.completed >= 0
            LIMIT 1',
            $id
        );
        $month = $this->SQL->fetch_value('
            SELECT MAX(d.value)
            FROM users_routes_link url
            JOIN routes r
                ON r.id = url.route_id
            JOIN difficulties d
                ON d.id = r.difficulty_id
            WHERE url.user_id = %d
            AND url.completed >= 0
            AND url.timestamp >= DATE_SUB(NOW(), INTERVAL 1 MONTH)
            LIMIT 1',
            $id
        );
        $week = $this->SQL->fetch_value('
            SELECT MAX(d.value)
            FROM users_routes_link url
            JOIN routes r
                ON r.id = url.route_id
            JOIN difficulties d
                ON d.id = r.difficulty_id
            WHERE url.user_id = %d
            AND url.completed >= 0
            AND url.timestamp >= DATE_SUB(NOW(), INTERVAL 7 DAY)
            LIMIT 1',
            $id
        );
        return array(
            'all' => ($alltime ? $difficulties[$alltime] : 'none'),
            'month' => ($month ? $difficulties[$month] : 'none'),
            'week' => ($week ? $difficulties[$week] : 'none'),
        );
    }

    /*
     * getUserAvgDifficultyCompleted - get the average difficulty route a user has successfully climbed
     * @param id int user id
     * @return int difficulty id (not name)
     */
    public function getUserAvgDifficultyCompleted($id) {
        $diff = $this->diffMgr->getAll();
        $difficulties = array();
        foreach($diff as $row) {
            $difficulties[$row['value']] = $row['name'];
        }

        $alltime = $this->SQL->fetch_value('
            SELECT AVG(d.value)
            FROM users_routes_link url
            JOIN routes r
                ON r.id = url.route_id
            JOIN difficulties d
                ON d.id = r.difficulty_id
            WHERE url.user_id = %d
            AND url.completed >= 0
            LIMIT 1',
            $id
        );
        $month = $this->SQL->fetch_value('
            SELECT AVG(d.value)
            FROM users_routes_link url
            JOIN routes r
                ON r.id = url.route_id
            JOIN difficulties d
                ON d.id = r.difficulty_id
            WHERE url.user_id = %d
            AND url.completed >= 0
            AND url.timestamp >= DATE_SUB(NOW(), INTERVAL 1 MONTH)
            LIMIT 1',
            $id
        );
        $week = $this->SQL->fetch_value('
            SELECT AVG(d.value)
            FROM users_routes_link url
            JOIN routes r
                ON r.id = url.route_id
            JOIN difficulties d
                ON d.id = r.difficulty_id
            WHERE url.user_id = %d
            AND url.completed >= 0
            AND url.timestamp >= DATE_SUB(NOW(), INTERVAL 7 DAY)
            LIMIT 1',
            $id
        );
        return array(
            'all' => ($alltime ? $difficulties[(int)$alltime] : 'none'),
            'month' => ($month ? $difficulties[(int)$month] : 'none'),
            'week' => ($week ? $difficulties[(int)$week] : 'none'),
        );
    }

    /*
     * getCheckin - get information on a single checkin
     * @param id int checkin id
     * @param userId int user id that owns the checkin
     * @return array of form array(id, user_id, route_id, timestamp, completed, comment, username, location_id, name, is_short, location, difficulty, colour)
     */
    public function getCheckin($id, $userId) {
        return $this->SQL->fetch_single('
            SELECT url.id, url.user_id, url.route_id, url.timestamp, url.completed, url.comment,
                u.name AS username, r.location_id, r.name, r.is_short, l.name AS location, d.name AS difficulty, c.name AS colour
            FROM users_routes_link url
            JOIN users u
                ON u.id = url.user_id
            JOIN routes r
                ON r.id = url.route_id
            JOIN locations l
                ON l.id = r.location_id
            JOIN difficulties d
                ON d.id = r.difficulty_id
            JOIN colours c
                ON c.id = r.colour_id
            WHERE url.id = %d
            AND url.user_id = %d
            LIMIT 1',
            $id,
            $userId
        );
    }

    /*
     * deleteCheckin - delete a checkin of your own
     * @param id int checkin id
     * @param userId int user id
     * @return bool TRUE/FALSE
     */
    public function deleteCheckin($id, $userId) {
        return $this->SQL->fquery('
            DELETE FROM users_routes_link
            WHERE id = %d
            AND user_id = %d
            LIMIT 1',
            $id,
            $userId
        );
    }

    /*
     * follow - add a friend (follow his updates)
     * @param id int user id
     * @param friendId int user id to follow
     * @return bool TRUE/FALSE
     */
    public function follow($id, $friendId) {
        if($id == $friendId) {
            return FALSE;
        }
        $ret = $this->SQL->fquery('
            INSERT IGNORE INTO friends
            (user_id, friend_id)
            VALUES
            (%d, %d)',
            $id,
            $friendId
        );
        if($ret) {
           $follower = $this->get($id);
           $data = $this->get($friendId);
           $this->msgMgr->sendFollowMail($data, $follower);
        }
        return $ret;
    }

    /*
     * unfollow - remove a friend (stop following his updates)
     * @param id int user id
     * @param friendId int user id to unfollow
     * @return bool TRUE/FALSE
     */
    public function unfollow($id, $friendId) {
        return $this->SQL->fquery('
            DELETE FROM friends
            WHERE user_id = %d
            AND friend_id = %d
            LIMIT 1',
            $id,
            $friendId
        );
    }

    /*
     * validateResetPasswordHash - validate that the hash given is valid for any user
     * @param hash string password reset hash
     * @return bool TRUE/FALSE
     */
    public function validateResetPasswordHash($hash) {
        $id = $this->SQL->fetch_value('
            SELECT id
            FROM users
            WHERE SHA(CONCAT(username, "%1$s")) = "%2$s"
            LIMIT 1',
            PASSWORD_SALT,
            addslashes($hash)
        );
        if($id) {
            return $this->get($id);
        } else {
            return FALSE;
        }
    }

    /*
     * resetPassword - change password for given account
     * @param id int user id
     * @param string new password
     * @return bool TRUE/FALSE
     */
    public function resetPassword($id, $password) {
        return $this->SQL->fquery('
            UPDATE users
            SET password = SHA(CONCAT("%1$s", "%2$s"))
            WHERE id = %3$d
            LIMIT 1',
            $password,
            PASSWORD_SALT,
            $id
        );
    }

    /*
     * statsGetUserCompleted - generate a piechart of completed/block/failed total route count for a user
     * @param userId int user id
     * @return image
     */
    public function statsGetUserCompleted($userId) {
        $stats = $this->getUserRouteStats($userId);
        if(!$stats || !$stats['count']) {
            emptyimage();
        }

        /* Create and populate the pData object */
        $MyData = new pData();
        $MyData->addPoints(array($stats['completed'] - $stats['blocked'], $stats['blocked'], $stats['count'] - $stats['completed']),'ScoreA');
        $MyData->setSerieDescription('ScoreA','Route completion');

        /* Define the abscissa serie */
        $MyData->addPoints(array('Completed', 'Block', 'Failed'), 'Labels');
        $MyData->setAbscissa('Labels');

        $myCache = new pCache();
        $chartHash = $myCache->getHash($MyData);
        if($myCache->isInCache($chartHash) && !NOCACHE) {
            $myCache->autoOutput($chartHash, 'default.png');
            return;
        }

        /* Create the pChart object */
        $myPicture = new pImage(275, 180, $MyData, TRUE);

        /* Set the default font properties */
        $myPicture->setFontProperties(array(
            'FontName' => './lib/pchart/fonts/verdana.ttf',
            'FontSize' => 10,
            'R' => 80, 'G' => 80, 'B' => 80,
        ));

        /* Create the pPie object */
        $PieChart = new pPie($myPicture, $MyData);

        /* Enable shadow computing */
        $myPicture->setShadow(TRUE, array(
            'X' => 3, 'Y' => 3,
            'R' => 0, 'G' => 0, 'B' => 0,
            'Alpha' => 10,
        ));

        /* Draw a splitted pie chart */
        $PieChart->draw3DPie(120, 90, array(
            'Radius' => 110,
            'Border' => TRUE,
            'WriteValues' => PIE_VALUE_NATURAL,
            'ValueR' => 50, 'ValueG' => 50, 'ValueB' => 50,
        ));

        /* Write the legend box */
        $myPicture->setFontProperties(array(
            'FontName' => './lib/pchart/fonts/verdana.ttf', 'FontSize' => 9,
            'R' => 0, 'G' => 0, 'B' => 0,
        ));
        $PieChart->drawPieLegend(30, 160, array(
            'Style' => LEGEND_NOBORDER,
            'Mode' => LEGEND_HORIZONTAL,
        ));

        $myCache->writeToCache($chartHash, $myPicture);
        $myPicture->autoOutput('pictures/example.draw3DPie.transparent.png');
    }

    /*
     * statsGetUserDifficultyRange - generate graph for difficulty range of routes climbed for a user
     * @param userId int user id
     * @return image
     */
    public function statsGetUserDifficultyRange($userId) {
        $data = $this->getUserDifficultyRange($userId);
        if(!$data) {
            emptyimage();
        }

        $diffMgr = new Difficulty();
        $difficulties = $diffMgr->getAll();
        $labeledData = array();
        $labels = array();
        foreach($data as $row) {
            $labeledData[$row['id']] = $row['count'];
            $labels[$row['id']] = $difficulties[$row['id']]['name'];
        }

        /* Create and populate the pData object */
        $MyData = new pData();
        $MyData->addPoints(array_values($labeledData), 'count');
        $MyData->setAxisName(0, 'Count');
        $MyData->addPoints(array_values($labels), 'diff_range');
        $MyData->setSerieDescription('diff_range', 'Difficulty');
        $MyData->setAbscissa('diff_range');
        $MyData->setAbscissaName('Difficulty');
        $MyData->setAxisDisplay(0, AXIS_FORMAT_METRIC, 1);

        $myCache = new pCache();
        $chartHash = $myCache->getHash($MyData);
        if($myCache->isInCache($chartHash) && !NOCACHE) {
            $myCache->autoOutput($chartHash, 'default.png');
            return;
        }

        $chartWidth = 275;
        $chartHeight = 45 + 36 * count($data);
        $chartMarginX = 50;
        $chartMarginY = 28;

        $myPicture = new pImage($chartWidth, $chartHeight, $MyData);
        $myPicture->setFontProperties(array('FontName' => './lib/pchart/fonts/verdana.ttf', 'FontSize' => 10));
        $myPicture->setGraphArea($chartMarginX, $chartMarginY, $chartWidth - $chartMarginX / 2, $chartHeight - $chartMarginY / 2);
        $myPicture->drawScale(array(
            'CycleBackground' => TRUE,
            'GridR' => 0, 'GridG' => 0, 'GridB' => 0,
            'GridAlpha' => 10,
            'Pos' => SCALE_POS_TOPBOTTOM,
            'Mode' => SCALE_MODE_START0,
        ));
        $myPicture->setShadow(TRUE, array(
            'X' => 1, 'Y' => 1,
            'R' => 0, 'G' => 0, 'B' => 0,
            'Alpha' => 10,
        ));
        $myPicture->drawBarChart(array(
            'DisplayPos' => LABEL_POS_INSIDE,
            'DisplayValues' => TRUE,
            'Rounded' => TRUE,
            'Surrounding' => 30,
        ));

        $myCache->writeToCache($chartHash, $myPicture);
        $myPicture->autoOutput('default.png');
    }

    /*
     * getAllUsers - get all user records and their checkin counts
     * @return array of form (id, username, passwordhash, name, registration date, checkin count)
     */
    public function getAllUsers() {
        return $this->SQL->fquery('
            SELECT u.id, u.username, u.password, u.name, u.registered, COALESCE(COUNT(url.id), 0) AS count
            FROM users u
            LEFT JOIN users_routes_link url
                ON url.user_id = u.id
            GROUP BY u.id
            ORDER BY id'
        );
    }

    /*
     * getUserCompletionStats - get completion stats for a given user for a given location
     * @param userId int user id
     * @param locationid int location id
     * @return array of form (difficulty, count, climbed, completed)
     */
    public function getUserCompletionStats($userId, $locationId) {
        $ret = $this->SQL->fquery('
            SELECT d.name AS difficulty, COUNT(DISTINCT r.id) AS `count`, COUNT(DISTINCT url.route_id) AS climbed
            FROM routes r
            JOIN difficulties d
                ON d.id = r.difficulty_id
            LEFT JOIN users_routes_link url
                ON url.route_id = r.id
                AND url.user_id = %d
                AND url.completed >= 0
            WHERE r.location_id = %d
            AND r.removed = 0
            GROUP BY r.difficulty_id
            ORDER BY d.value',
            $userId,
            $locationId
        );
        $nothingClimbed = TRUE;
        foreach($ret as $key => $row) {
            if($row['climbed'] > 0) {
                $nothingClimbed = FALSE;
            }
            $ret[$key]['completed'] = round($row['climbed'] * 100 / $row['count']);
        }
        return ($nothingClimbed ? FALSE : $ret);
    }

    /*
     * statsGetUserCompletionChart - create graph of user completion stats for a location
     * @param userId int user id
     * @param locationId int location id
     * @return void (outputs graph)
     */
    public function statsGetUserCompletionChart($userId, $locationId) {
        $locationMgr = new Location();
        $locationDiffRange = $locationMgr->getDifficultyRange($locationId);
        $minDiff = array();
        $maxDiff = array();
        foreach($locationDiffRange as $row) {
            if(!isset($minDiff['value']) || $row['value'] < $minDiff['value']) {
                $minDiff = $row;
            }
            if(!isset($maxDiff['value']) || $row['value'] > $minDiff['value']) {
                $maxDiff = $row;
            }
        }
        $data = array();
        foreach($this->getUserCompletionStats($userId, $locationId) as $diff) {
            $data[$diff['difficulty']] = $diff['completed'];
        }
        function statsCompletionYAxis($value) {
            return ($value > 0 ? $value . '%' : '');
        }

        /* Create and populate the pData object */
        $MyData = new pData();
        $MyData->addPoints(array_values($data), 'percentage');
        $MyData->setAxisName(0, 'Percentage');
        $MyData->addPoints(array_keys($data), 'diff_range');
        $MyData->setSerieDescription('diff_range', 'Difficulty');
        $MyData->setAbscissa('diff_range');
        $MyData->setAbscissaName('Difficulty');
        $MyData->setAxisDisplay(0, AXIS_FORMAT_CUSTOM, 'statsCompletionYAxis');

        $myCache = new pCache();
        $chartHash = $myCache->getHash($MyData);
        if($myCache->isInCache($chartHash) && !NOCACHE) {
            $myCache->autoOutput($chartHash, 'default.png');
            return;
        }

        $chartWidth = 275;
        $chartHeight = 45 + 32 * count($data);
        $chartMarginX = 50;
        $chartMarginY = 28;

        $myPicture = new pImage($chartWidth, $chartHeight, $MyData);
        $myPicture->setFontProperties(array('FontName' => './lib/pchart/fonts/verdana.ttf', 'FontSize' => 10));
        $myPicture->setGraphArea($chartMarginX, $chartMarginY, $chartWidth - $chartMarginX / 2, $chartHeight - $chartMarginY / 2);
        $myPicture->drawScale(array(
            'Mode' => SCALE_MODE_MANUAL,
            'ManualScale' => array(1 => array('Min' => $minDiff['name'], 'Max' => $maxDiff['name']), 0 => array('Min' => 0, 'Max' => 100)),
            'YMargin' => 5,
            'CycleBackground' => TRUE,
            'GridR' => 0, 'GridG' => 0, 'GridB' => 0,
            'GridAlpha' => 10,
            'Pos' => SCALE_POS_TOPBOTTOM,
        ));
        $myPicture->setShadow(TRUE, array(
            'X' => 1, 'Y' => 1,
            'R' => 0, 'G' => 0, 'B' => 0,
            'Alpha' => 10,
        ));
        $myPicture->drawBarChart(array(
            'DisplayPos' => LABEL_POS_INSIDE,
            'DisplayValues' => TRUE,
            'Rounded' => TRUE,
            'Surrounding' => 30,
        ));
        $myPicture->drawLegend(570, 215, array(
            'Style' => LEGEND_NOBORDER,
            'Mode' => LEGEND_HORIZONTAL,
        ));

        $myCache->writeToCache($chartHash, $myPicture);
        $myPicture->autoOutput('default.png');
    }

    public function getWeekStats($userId, $limit = 10) {
        return $this->SQL->fquery('
            SELECT ROUND(AVG(d.value)) AS avg_diff, MAX(d.value) AS max_diff, WEEKOFYEAR(url.timestamp) AS week
            FROM users_routes_link url
            JOIN routes r
                ON r.id = url.route_id
            JOIN difficulties d
                ON d.id = r.difficulty_id
            WHERE url.user_id = %d
            AND url.completed >= 0
            GROUP BY week
            ORDER BY week
            LIMIT %d',
            $userId,
            $limit
        );
    }

    public function statsGetWeekStats($userId, $limit = 10) {
        $data = $this->getWeekStats($userId, $limit);
        if(!$data) {
            emptyimage();
        }

        $diffMgr = new Difficulty();
        $difficulties = $diffMgr->getAll();
        global $diffVal2Name;
        $diff2ValName = array();
        foreach($difficulties as $row) {
            $diffVal2Name[$row['value']] = $row['name'];
        }

        $avg_data = array();
        $max_data = array();
        foreach($data as $row) {
            $avg_data[$row['week']] = $row['avg_diff'];
            $max_data[$row['week']] = $row['max_diff'];
        }

        function statsWeekStatsYAxis($value) {
            global $diffVal2Name;
            return (isset($diffVal2Name[$value]) && (int)$value == $value ? $diffVal2Name[$value] : '');
        }

        /* Create and populate the pData object */
        $MyData = new pData();
        $MyData->addPoints(array_values($max_data), 'Maximum');
        $MyData->addPoints(array_values($avg_data), 'Average');
        $MyData->setSerieWeight('Maximum', 2);
        $MyData->setSerieTicks('Average', 4);
        $MyData->setAxisName(0, 'Difficulty');
        $MyData->setAxisDisplay(0, AXIS_FORMAT_CUSTOM, 'statsWeekStatsYAxis');
        $MyData->addPoints(array_keys($avg_data), 'weeks');
        $MyData->setSerieDescription('weeks','Week number');
        $MyData->setAbscissa('weeks');
        $MyData->setAbscissaName('Week number');

        /* Create the pChart object */
        $chartWidth = 275;
        $chartHeight = 45 + 40 * count($data);
        $chartMarginX = 50;
        $chartMarginY = 45;
        $chartMarginYBottom = 28;

        $myCache = new pCache();
        $chartHash = $myCache->getHash($MyData);
        if($myCache->isInCache($chartHash) && !NOCACHE) {
            $myCache->autoOutput($chartHash, 'default.png');
            return;
        }

        $myPicture = new pImage($chartWidth, $chartHeight, $MyData);
        $myPicture->Antialias = FALSE;
        $myPicture->setFontProperties(array('FontName' => './lib/pchart/fonts/verdana.ttf', 'FontSize' => 10));
        $myPicture->setGraphArea($chartMarginX, $chartMarginY, $chartWidth - $chartMarginX / 2, $chartHeight - $chartMarginYBottom / 2);

        /* Draw the scale */
        $scaleSettings = array(
            'XMargin' => 3,
            'YMargin' => 0,
            'GridR' => 200,
            'GridG' => 200,
            'GridB' => 200,
            'DrawSubTicks' => TRUE,
            'SubTickR' => 0, 'SubTickG' => 0, 'SubTickB' => 0,
            'CycleBackground' => TRUE,
            'LabelingMethod' => LABELING_DIFFERENT,
            'Pos'  => SCALE_POS_TOPBOTTOM,
        );
        $myPicture->drawScale($scaleSettings);
        $myPicture->Antialias = TRUE;
        $myPicture->setShadow(TRUE, array('X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10));

        $myPicture->drawLineChart();
        $myPicture->drawPlotChart(array('DisplayValues' => FALSE, 'PlotBorder' => TRUE, 'BorderSize' => 2, 'Surrounding' => -60, 'BorderAlpha' => 80));
        $myPicture->drawLegend(5, 5, array('Style' => LEGEND_NOBORDER, 'Mode' => LEGEND_VERTICAL));

        $myCache->writeToCache($chartHash, $myPicture);
        $myPicture->autoOutput('default.png');
    }

    public function loadGravatar($email) {
        return 'http://www.gravatar.com/avatar/' . md5(strtolower(trim($email))) . '?s=20&d=mm';
    }
}
?>
